﻿using RayDen.Library.Core.Primitives;
using RayDen.RayEngine;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data;

namespace RayDen.RayEngine.Engines.ProgressivePhotonMapping
{
    public class PPMLightPath : BasePPMPath
    {
        private RgbSpectrum ThroughtPut;
        public float Alpha = 0.75f;

        public override void InitPath(IPathProcessor buffer)
        {
            //Choose lightsource
            //Sample it
            base.InitPath(buffer);
            this.Sample = new Sample(pathIntegrator.Sampler);
            this.RayIndex = -1;
            float lspdf;

            int li = scene.SampleLights(this.Sample.GetLazyValue(), out lspdf);
            RayData ray;

            var ls = new LightSample();
            bool done = false;
            while (!done)
            {
                scene.Lights[li].EvaluatePhoton(
                    scene,
                    Sample.GetLazyValue(),
                    Sample.GetLazyValue(),
                    Sample.GetLazyValue(),
                    Sample.GetLazyValue(),
                    Sample.GetLazyValue(),
                    out ls);

                done = ls.Pdf > 0f;

            }

            this.pathWeight = 10000 /(ls.Pdf * lspdf);
            ls.Spectrum.Mul(pathWeight);
            this.ThroughtPut = new RgbSpectrum(ls.Spectrum.ToArray());
            this.PathRay = ls.LightRay;
        }

        public override void Advance(RayBuffer rayBuffer, SampleBuffer consumer)
        {
            base.Advance(rayBuffer, consumer);
            var rayHit = rayBuffer.rayHits[RayIndex];
            depth++;
            bool missed = rayHit.Miss();
            if (missed)
            {
                this.InitPath(pathIntegrator);
                return;
            }

            var hitPoint = PathRay.Point(rayHit.Distance);
            if (hitInfo == null)
            {
                hitInfo = new SurfaceIntersectionData(ref rayHit, true);
            }
            SurfaceSampler.GetIntersection(ref PathRay, ref rayHit, ref hitInfo);
            if (hitInfo.IsLight)
            {
                this.InitPath(pathIntegrator);
                engine.IncreasePhoton();
                return;
            }

            float fPdf;
            Vector wi, wo = -PathRay.Dir;
            var bsdf = hitInfo.MMaterial;
            RgbSpectrum f = bsdf.Sample_f(ref wo, out wi, ref hitInfo.Normal, ref hitInfo.ShadingNormal, ref ThroughtPut,
                                                    Sample.GetLazyValue(), Sample.GetLazyValue(),
                                                    Sample.GetLazyValue(), ref hitInfo.TextureData,
                                                    out fPdf, out specularBounce);
            if (fPdf <= 0f || f.IsBlack())
            {
                this.InitPath(pathIntegrator);
                engine.IncreasePhoton();
                return;
            }


            if (!specularBounce)
            {
                engine.StoreFlux(Alpha, ref hitPoint, ref hitInfo.ShadingNormal, wi, ref ThroughtPut);
                //InitPath(pathIntegrator);
                //return;
            }

            ThroughtPut *= f / fPdf;

            var p = scene.RussianRuletteImportanceCap;

            if (depth < scene.MaxPathDepth)
            {
                PathRay = new RayData(hitPoint, wi);
            }
            else if (Sample.GetLazyValue() < p)
            {
                ThroughtPut /= p;
                PathRay = new RayData(hitPoint, wi);
            }
            else
            {
                // Re-initialize the photon path
                engine.IncreasePhoton();
                InitPath(pathIntegrator);
            }
        }

        public override void Splat(SampleBuffer sampleBuffer)
        {

        }
    }


}